#ifndef __AST_H__
#define __AST_H__

#include <fstream>

class SymbolEntry;
class Type;

class Node
{
private:
    static int counter;
    int seq;
public:
    Node();
    int getSeq() const {return seq;};
    virtual void output(int level) = 0;
};

class ExprNode : public Node
{
protected:
    SymbolEntry *symbolEntry;
public:
    ExprNode(SymbolEntry *symbolEntry) : symbolEntry(symbolEntry){};
    // void output(int level);
};

class BinaryExpr : public ExprNode
{
private:
    int op;
    ExprNode *expr1, *expr2;
public:
    enum {ADD, SUB, AND, OR, LESS, MORE, LE , ME , EQ , UE, DIV, MUL, MOD, UMINUS};
    BinaryExpr(SymbolEntry *se, int op, ExprNode*expr1, ExprNode*expr2) : ExprNode(se), op(op), expr1(expr1), expr2(expr2){};
    void output(int level);
};

class Constant : public ExprNode
{
public:
    Constant(SymbolEntry *se) : ExprNode(se){};
    void output(int level);
};

class Id : public ExprNode
{
public:
    Id(SymbolEntry *se) : ExprNode(se){};
    void output(int level);
};

class StmtNode : public Node
{};

class CompoundStmt : public StmtNode
{
private:
    StmtNode *stmt;
public:
    CompoundStmt(StmtNode *stmt) : stmt(stmt) {};
    void output(int level);
};

class CommentStmt : public StmtNode
{
private:
    std::string str;
public:
    CommentStmt(std::string s) {
        str=s;
    };
    void output(int level);
};

class SeqNode : public StmtNode
{
private:
    StmtNode *stmt1, *stmt2;
public:
    SeqNode(StmtNode *stmt1, StmtNode *stmt2) : stmt1(stmt1), stmt2(stmt2){};
    void output(int level);
};

class DeclStmt : public StmtNode
{
private:
    Id *id;
public:
    DeclStmt(Id *id) : id(id){};
    void output(int level);
};

class IdRefList : public StmtNode
{
private:
    ExprNode *ref1;
    std::string refv;
public:
    IdRefList(ExprNode *ref1 ,std::string refv) : ref1(ref1),refv(refv){};
    void output(int level);
};

class FuncParaList : public StmtNode
{
private:
    ExprNode *stmt1 ;
    std::string paramlist;
public:
    FuncParaList(ExprNode *stmt1, std::string paramlist) : stmt1(stmt1), paramlist(paramlist){};
    void output(int level);
};

class IfStmt : public StmtNode
{
private:
    ExprNode *cond;
    StmtNode *thenStmt;
public:
    IfStmt(ExprNode *cond, StmtNode *thenStmt) : cond(cond), thenStmt(thenStmt){};
    void output(int level);
};

class IfElseStmt : public StmtNode
{
private:
    ExprNode *cond;
    StmtNode *thenStmt;
    StmtNode *elseStmt;
public:
    IfElseStmt(ExprNode *cond, StmtNode *thenStmt, StmtNode *elseStmt) : cond(cond), thenStmt(thenStmt), elseStmt(elseStmt) {};
    void output(int level);
};

class WhileStmt : public StmtNode
{
private:
    ExprNode *cond;
    StmtNode *whileStmt;
public: 
    WhileStmt(ExprNode *cond, StmtNode *whileStmt) : cond(cond), whileStmt(whileStmt){};
    void output(int level);
};

class ReturnStmt : public StmtNode
{
private:
    ExprNode *retValue;
public:
    ReturnStmt(ExprNode*retValue) : retValue(retValue) {};
    void output(int level);
};

class AssignStmt : public StmtNode
{
private:
    ExprNode *lval;
    ExprNode *expr;
public:
    AssignStmt(ExprNode *lval, ExprNode *expr) : lval(lval), expr(expr) {};
    void output(int level);
};

class DeclAssignStmt : public StmtNode
{
private:
    Id *id;
    ExprNode *expr;
public:
    DeclAssignStmt(Id *id,ExprNode *expr) : id(id), expr(expr) {};
    void output(int level);
};

class DeclIdListStmt : public StmtNode
{
private:
    std::string stringv;
    Type *type;
    
public:
    DeclIdListStmt(std::string stringv,Type *type):stringv(stringv),type(type){};
    void output(int level);
};

class NonAssignStmt : public StmtNode
{
private:
    ExprNode *lval;
    ExprNode *expr;
public:
    NonAssignStmt(ExprNode *expr) : expr(expr) {};
    NonAssignStmt(){};
    void output(int level);
};

class FunctionDef : public StmtNode
{
private:
    SymbolEntry *se;
    StmtNode *paramlist;
    StmtNode *stmt;
public:
    FunctionDef(SymbolEntry *se, StmtNode *paramlist, StmtNode *stmt) : se(se), paramlist(paramlist) , stmt(stmt){};
    FunctionDef(SymbolEntry *se, StmtNode *stmt) : se(se), stmt(stmt){};
    void output(int level);
};

class FunctionRef : public ExprNode
{
private:
    StmtNode *paramlist;
public:
    FunctionRef(SymbolEntry *se) : ExprNode(se){};
    FunctionRef(SymbolEntry *se , StmtNode* paramlist) : ExprNode(se),paramlist(paramlist){};
    void output(int level);
};
class Ast
{
private:
    Node* root;
public:
    Ast() {root = nullptr;}
    void setRoot(Node*n) {root = n;}
    void output();
};

#endif
